2.7.2 ⁂Iterating Over a Comma-Separated List
The etoolbox package [51] provides:
This iterates over the given comma-separated list and does
at each iteration, where ⟨item⟩ is the current item in the list.
It's up to the user to define \do
before using
\docsvlist
. For example:
produces:
Alternatively you can provide your own handler instead of \do
using
For example:
which again produces:
\docsvlist
(and \forcsvlist
)
doesn't get expanded, so if you try:
\newcommand*{\mylist}{Parrot,Canary,Zebra,Arara,Duck}% \renewcommand{\do}[1]{#1. }% ✘\docsvlist{\mylist}
then you'll only have a list with a single item (\mylist
) so
you'll just have the one iteration
which just produces:
Instead you need to make sure the argument is expanded before it's
processed by \docsvlist
:
\newcommand*{\mylist}{Parrot,Canary,Zebra,Arara,Duck}% \renewcommand{\do}[1]{#1. }% ✔\expandafter\docsvlist\expandafter{\mylist}
The \expandafter
commands may look a bit confusing but the syntax is
This makes TeX expand ⟨token 2⟩ before it processes ⟨token 1⟩, so it's equivalent to:
\expandafter | \docsvlist | \expandafter |
↑ | ↑ | |
⟨token 1⟩ | ⟨token 2⟩ |
means that TeX must expand the thing after \docsvlist
before it does \docsvlist
(step ① in
Figure 2.9), but that thing happens to be
another \expandafter
:
\expandafter | { | \mylist |
↗ | ↑ | |
⟨token 1⟩ | ⟨token 2⟩ |
This means that before TeX processes the left brace character
{ it must first expand the token after it
(step ②), so that
\mylist
is replaced with its definition (step ③).
So TeX starts out with the first \expandafter
, skips over
\docsvlist
① and does the second \expandafter
which
makes TeX skip over the open brace ② and expand \mylist
,
which replaces \mylist
with its definition ③.
Once \mylist
has been expanded, TeX then goes back to the
\docsvlist
command, which is now in the form:
In the case of \forcsvlist
, this becomes more complicated as
the list is the second argument. However, since the first argument is
just a single control sequence it doesn't need to be grouped and can therefore
be skipped over with another \expandafter
. For example:
\newcommand*{\mylist}{Parrot,Canary,Zebra,Arara,Duck}% \newcommand{\mylistitem}[1]{#1. }% \expandafter\forcsvlist\expandafter\mylistitem\expandafter{\mylist}
The datatool package also provides some comma-separated list related commands:
This checks if the given item is in ⟨list⟩. A one-level expansion is performed on ⟨list⟩ but not on ⟨item⟩.
This counts the number of non-empty items in ⟨list⟩ and stores the result in the control sequence ⟨cs⟩. Again, a one-level expansion is performed on ⟨list⟩.
Example:
\newcommand{\mylist}{Parrot,Canary,Zebra,Arara,Duck}% Parrot \DTLifinlist{Parrot}{\mylist}{is}{isn't} in the list. Number of items in the list: \DTLnumitemsinlist{\mylist}{\numitems}\numitems.
produces:
Spaces
Remember what I mentioned earlier about being careful of spaces? Here's an illustration of unexpected behaviour involving spaces in lists:
This produces:
The leading spaces have been ignored but the trailing spaces are still present.
The LaTeX kernel has a command that can also iterate through a comma-separated list but it's an internal command:
This iterates through ⟨list⟩ and assigns the control sequence
⟨cs⟩ to the current item in the list so that it can be used as
a placeholder in ⟨body⟩. Note that in this context
\do
doesn't refer to etoolbox's \do
handler
macro but is used as an argument delimiter (so it's like plain TeX syntax rather than LaTeX syntax). Since \@for
is an internal command it should only be used in a package or class file. If it has to be
used in the document, it should be placed between \makeatletter
and \makeatother
like this:
\makeatletter \@for\thisitem:=Parrot,Canary,Zebra,Arara,Duck\do{\thisitem. } \makeatother
This produces:
The \@for
command expands the list so:
\newcommand*{\mylist}{Parrot,Canary,Zebra,Arara,Duck}% \makeatletter \@for\thisitem:=\mylist\do{\thisitem. } \makeatother
produces the same result as above. However, now let's look at what happens when we introduce spaces into the list again:
\makeatletter \@for\thisitem:= Parrot , Canary , Zebra , Arara , Duck \do {``\thisitem''. } \makeatother
This produces:
In this case both the leading and trailing spaces have been retained.
The etextools package [15] also provides commands to iterate over lists. For example:
The \csvloop
macro iterates over the comma-separated list given in
⟨list⟩ (which may be a macro that expands to a list) and at
each iteration does ⟨auxiliary commands⟩. If this optional
argument is missing, \do
is assumed, which behaves in the same
way as with etoolbox's \docsvlist
command. So
produces
Now let's try with spaces again:
This produces:
In this case the leading space has been retained on the first item, but not on any of the other items, but all the trailing spaces have been retained.
\forlistloop
but the syntax is
incompatible.
Since datatool automatically loads
etoolbox this means that datatool and etextools may also
conflict.
Another package is pgffor (part of the pgf bundle [102]) which provides:
where ⟨list⟩ is either an explicit comma-separated list or a control sequence that expands to a comma-separated list. The syntax can get quite complicated, but the simplest version is in the form:
which produces:
Now let's try with spaces:
This produces:
which is the same result as with \docsvlist
where the trailing
spaces have been retained but not the leading spaces.
This is why it's important to ensure any spurious spaces are removed from
comma-separated lists in your source code. It may be that some commands trim all
spaces (for example in the optional argument of \usepackage
or
\documentclass
),6 while some commands only trim leading spaces (as with
\docsvlist
and \foreach
) but others don't trim any spaces (such
as \cite
, which internally uses \@for
). Remember that you can comment
out space caused by the EOL character, and spaces at the start of lines
are automatically discarded by TeX, so to make your code clearer you can do,
for example:
If you comment an EOL character that would naturally be discarded (for example, following a control sequence) there's no harm done, but if you forget to comment an unwanted EOL character, you can end up with weird spaces in your document or an error message.
Empty Items
In addition to watching out for spurious spaces, you also need to consider what happens if you have an empty item in your list. Do you want empty items skipped or should they be processed either in the same way as the other items or by displaying a missing data symbol, such as an em-dash? Let's look at how the above comma-separated list processing commands deal with this type of situation:
\renewcommand{\do}[1]{``#1''. } \docsvlist{,Parrot,Canary,Zebra,,Arara,Duck} \csvloop{,Parrot,Canary,Zebra,,Arara,Duck} \foreach\thisitem in {,Parrot,Canary,Zebra,,Arara,Duck} {``\thisitem''. } \makeatletter \@for\thisitem:=,Parrot,Canary,Zebra,,Arara,Duck\do {``\thisitem''. } \makeatother
The result is:
So \docsvlist
and \csvloop
both skip empty items but
\foreach
and \@for
don't.
Create a document that defines the commands:
\newcommand*{\mylistI}{A,B,C,D} \newcommand*{\mylistII}{a,b,c,d}
then create the tabular
environment shown in
Table 2.8 using \docsvlist
to construct
columns 2 to 5 for each row.
Next, redefine \mylistII
so the third item is empty
and redo the tabular
environment. Finally, define a command
called \missingdata
that does nothing and redefine
\mylistII
so the third item is \missingdata
and
redo the tabular
environment.
Footnotes
- ...documentclass),6
- In fact, all spaces are stripped from
the optional argument of those two commands, so it's technically possible
to do, say,
\usepackage
[dr aft]{graphics}
although I don't recommend you do this.
This book is also available as A4 PDF or 12.8cm x 9.6cm PDF or paperback (ISBN 978-1-909440-07-4).